<?php

namespace App\Common\Util\ImitateAop\Aspects;

use App\Common\Exception\ApiMessageException;
use App\Common\Util\Lock\FileLock;
use App\Common\Util\Lock\RedisLock;
use App\Common\Util\TP;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Sc\Util\ImitateAopProxy\Interfaces\ImitateAspectInterface;
use think\facade\Cache;

/**
 * 锁
 *
 * Class LockAspect
 */
class LockAspect implements ImitateAspectInterface
{
    /**
     * @param string                      $lockSign
     * @param string                      $failMessage
     * @param int                         $waitTime
     * @param \ReflectionFunctionAbstract $reflectionFunctionAbstract
     */
    public function __construct(private string $lockSign, private readonly string $failMessage, private readonly int $waitTime, private readonly \ReflectionFunctionAbstract $reflectionFunctionAbstract)
    {}

    /**
     * @param \Closure $closure
     * @param array $args
     * @return mixed
     * @throws ApiMessageException
     */
    public function handle(\Closure $closure, array $args): mixed
    {
        $this->signAnalysis($args);
        $lockInstance = TP::config('cache.lock') ?: FileLock::class;

        if (!$lock = $lockInstance::up($this->lockSign, waitTime: $this->waitTime)){
            throw new ApiMessageException($this->failMessage);
        }

        try {
            $res = $closure($args);
        } finally {
            $lock->down();
        }

        return $res;
    }

    /**
     * @param array $args
     *
     * @return void
     */
    private function signAnalysis(array $args): void
    {
        $this->lockSign = preg_replace_callback('/\{(\w+)}/', function ($match) use ($args){
            foreach ($this->reflectionFunctionAbstract->getParameters() as $index => $parameter) {
                if ($parameter->getName() == $match[1]) {
                    return $args[$index] ?? '';
                }
            }
            return '';
        }, $this->lockSign);
    }
}